from kivy.logger import Logger

from irc.client import NickMask
from threading import Timer

from amgineirc.view.settings.trustedView import EditTrustedPopup, TrustedView
from amgineirc.viewModel.channels.sendReceiveViewModel import SendReceiveViewModel


class ChannelViewModel(SendReceiveViewModel):
    """
    """

    dcc_command = r'/dcc chat'
    prvmsg_command = r'/privmsg'
    whois_command = r'/whois'
    ignore_command = r'/ignore'
    ping_command = r'/ping'
    trust_command = 'Trust'
    untrust_command = 'Stop Trusting'

    trusted_commands = [dcc_command,
                        prvmsg_command,
                        whois_command,
                        ping_command,
                        untrust_command]

    not_trusted_commands = [prvmsg_command,
                            whois_command,
                            # ignore_command,
                            ping_command,
                            trust_command]

    state_parted = 0
    state_joining = 1
    state_joined = 2

    def __init__(self, view, send_view, receive_view, connection, target, appdata):
        SendReceiveViewModel.__init__(self,
                                      view,
                                      send_view,
                                      receive_view,
                                      connection,
                                      target,
                                      appdata)
        self._last_source = ''
        self._users = []
        self._ignores = []
        self._state = ChannelViewModel.state_parted

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value

    def user_part(self, nick):
        # called by ConnectionsViewModel
        if nick in self._users:
            self._users.remove(nick)

    def nick_change(self, old_nick, new_nick):
        # update the target if needed
        if old_nick == self._target:
            self._target = new_nick
        Logger.debug('ChannelViewModel.nick_change: nick %s, %s' % (old_nick, new_nick))
        # called by ConnectionsViewModel
        if old_nick in self._users:
            self._users.remove(old_nick)
        if new_nick not in self._users:
            self._users.append(new_nick)
        text = '%s is now %s.' % (old_nick, new_nick)
        message = self._format_new_log_entry('nick', old_nick, old_nick, [text])
        # add the message
        self._receive_view.vm_log(message)
        self._last_source = old_nick
        # show page as updated
        if self._appdata['connection_vm'] is not None:
            self._appdata['connection_vm'].page_updated(self.server, self._target)

    def handle_on_remove(self):
        if self.connected:
            self.handle_view_send('/part ' + self._target)
        SendReceiveViewModel.handle_on_remove(self)

    # users - a 3 step process

    def th_display_users(self, *unused_largs):
        # step 1
        # called by the tab header view model
        users = self._users[:]
        users.sort()
        trusted = self._appdata['trusted_users'].nick_list(self.server)
        lenu = len(users)
        for i in range(lenu):
            # clean the nick
            clean_nick = users[i]
            if clean_nick[0] == '@':
                clean_nick = clean_nick[1:]
            if clean_nick in trusted:
                users[i] = '%s (Trusted)' % (users[i])
        self.view.select_options('Channel Users',
                                 users,
                                 self.w_handle_user)

    def w_handle_user(self, nick, *largs):
        # step 2
        # display commands for a selected user
        parts = nick.split()
        is_trusted = len(parts) > 1
        commands = ChannelViewModel.trusted_commands if is_trusted else ChannelViewModel.not_trusted_commands
        # clean the nick
        clean_nick = parts[0]
        if clean_nick[0] == '@':
            clean_nick = clean_nick[1:]
        self.view.select_options('Commands for %s' % (parts[0]),
                                 commands,
                                 self.w_handle_user_command,  # handler
                                 clean_nick,  # handler largs
                                 back_handler=self.th_display_users)

    def w_handle_user_command(self, command, *largs):
        # step 3
        # perform a selected command with a selected user
        target = largs[0]
        # a command from a button
        if command == ChannelViewModel.trust_command:
            # trust the user
            # create a new trusted user record
            # and let the trusted view model handle it
            popup = EditTrustedPopup(TrustedView.edit_command,
                                     # name, real name
                                     target, '',
                                     # server, server options
                                     self.server, [],
                                     # rule, rule options
                                     '', self._appdata['rules'].data,
                                     # handler
                                     self._appdata['trusted_vm'].handle_editor_command)
            popup.open()
        elif command == ChannelViewModel.untrust_command:
            # untrust the user
            # let the trusted view model handle it
            self._appdata['trusted_vm'].handle_option_command(TrustedView.delete_command, target, self.server)
        elif command == ChannelViewModel.dcc_command:
            # dcc chat with the user
            # open a view for this new connection
            # let its vm start the listening
            if self.connected:
                self._appdata['client'].send_dcc_chat_request(self._connection, target)
        elif command == ChannelViewModel.prvmsg_command:
            # private message chat with the user
            # let the connection vm open a view for a privmsg with this user
            # this user will have to start typing in that view to send the first private message
            if self.connected:
                self._appdata['connection_vm'].add_privmsg_connection(self._connection, target)
        else:
            # send the command to the server
            if self.connected:
                SendReceiveViewModel.w_handle_command(self, command, target)

    # recieve view methods

    def handle_client_event(self, evtarget, evtype, evserver, evsource, evargs):
        """
        handle an event from the client.
        based on evtype determine if this is the correct target.
        store event and start the logger to the view.
        """
        # set the state
        if self.state == ChannelViewModel.state_joining:
            if evtype != 'connected' and evtype != 'exception':
                self.state = ChannelViewModel.state_joined
        if evtype == 'nick':
            # this may need removing since its handled in ServerViewModel.
            # nick change
            # update the list view and send a message to the view
            nmask = NickMask(evsource)
            old_nick = nmask.nick
            Logger.debug('ChannelViewModel.handle_client_event: nick %s, %s' % (old_nick, evtarget))
            if evtarget not in self._users:
                self._users.append(evtarget)
            if old_nick in self._users:
                self._users.remove(old_nick)
                # now if there is a privmsg open with old_nick then close it.
                self._appdata['connection_vm'].remove_connection_page(self.server, old_nick)
            message = '%s has changed his or her nick to %s.' % (old_nick, evtarget)
        elif evtype == 'endofnames':
            return
        elif evtype == 'namreply':
            # the nick of a user in the channel
            msg = ' '.join(evargs)
            names = msg.split(None)
            for i in names:
                if i not in self._users:
                    self._users.append(i)
            return
        elif evtype == 'join':
            # new user has joined this channel
            # add the new user to the list view only
            nmask = NickMask(evsource)
            nick = nmask.nick
            if nick not in self._users:
                self._users.append(nick)
            # tell the user that these nicks have joined
            msg = '%s has joined the channel.' % (nick)
            message = self._format_new_log_entry(evtype, evtarget, evsource, [msg])
        elif evtype == 'part':
            self.user_part(evsource)
            message = '%s has left the room.' % (evsource)
        elif evtype == 'exception':
            message = self._format_new_log_entry(evtype, evtarget, evsource, evargs)
        elif evtype == 'input':
            # a command that this user typed
            if evsource == self._last_source:
                # continue from the last log entry
                message = ' '.join(evargs)
            else:
                # start a new log entry
                # format the message with type, target, source
                message = self._format_new_log_entry(evtype, evtarget, evsource, evargs)
        elif evtype == 'connected':
            message = self._format_new_log_entry(evtype, evtarget, evsource, evargs)
        elif evtype == 'disconnected':
            message = self._format_new_log_entry(evtype, evtarget, evsource, evargs)
        else:
            if evsource == self._last_source:
                # continue from the last log entry
                message = ' '.join(evargs)
            else:
                # start a new log entry
                # format the message with type, target, source
                message = self._format_new_log_entry(evtype, evtarget, evsource, evargs)
        # add the message
        self._receive_view.vm_log(message)
        self._last_source = evsource
        # show page as updated
        if self._appdata['connection_vm'] is not None:
            self._appdata['connection_vm'].page_updated(self.server, self._target)

    def handle_on_connect(self, connection):
        if self.state == ChannelViewModel.state_parted:
            self.state = ChannelViewModel.state_joining
            SendReceiveViewModel.handle_on_connect(self, connection)
            message = 'You are now connected to %s.' % (self.server)
            self.handle_client_event(self.target, 'connected', self.server, self.server, [message])
            ##message = '/join ' + str(self.target)
            message = '/join ' + self.target
            self.handle_client_event(self.target, 'connected', self.server, self.server, [message])
            try:
                self._connection.join(self.target)
            except Exception as e:
                message = repr(e)
                self.handle_client_event(self._target,
                                         'exception',
                                         self.server,
                                         '',
                                         [message])

    def handle_on_disconnect(self):
        self.state = ChannelViewModel.state_parted
        message = 'You are now disconnected from %s.' % (self.server)
        self.handle_client_event(self.target, 'disconnected', self.server, self.server, [message])
        SendReceiveViewModel.handle_on_disconnect(self)

    # send view methods

    def handle_view_send(self, message):
        """ the user has entered text to send.
        send the message over the network.
        """
        if not self.connected:
            return
        try:
            m = message.strip()
            if self._handle_view_send_privmsg(m):
                return
            else:
                # let the super handle it
                if not SendReceiveViewModel.handle_view_send(self, m):
                    m = 'Unknown IRC command: "%s"' % (m)
                # display the command
                self.handle_client_event(self._target,
                                         'input',
                                         self.server,
                                         self._nick,
                                         [m])
        except Exception as e:
            message = repr(e)
            self.handle_client_event(self._target,
                                     'exception',
                                     self.server,
                                     '',
                                     [message])

            # private methods
